/*
 * Copyright © 2016-2018 European Support Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import {
    actionTypes,
    rules,
    dataRules,
    SyncStates
} from './MergeEditorConstants.js';
import cloneDeep from 'lodash/cloneDeep.js';
import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
import Configuration from 'sdc-app/config/Configuration.js';
import ItemsHelper from '../../common/helpers/ItemsHelper.js';
import { modalContentMapper } from 'sdc-app/common/modal/ModalContentMapper.js';
import {
    actionTypes as modalActionTypes,
    modalSizes
} from 'nfvo-components/modal/GlobalModalConstants.js';
import i18n from 'nfvo-utils/i18n/i18n.js';
import { optionsInputValues as laOptionsValues } from 'sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js';
import { optionsInputValues as processOptionValues } from 'sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesConstants.js';
import { selectValues as limitSelectValues } from 'sdc-app/onboarding/licenseModel/limits/LimitEditorConstants.js';
import FeatureGroupsActionHelper from 'sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js';
import LicenseAgreementActionHelper from 'sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js';
import moment from 'moment';
import { DATE_FORMAT } from 'sdc-app/onboarding/OnboardingConstants.js';
import ScreensHelper from 'sdc-app/common/helpers/ScreensHelper.js';

function softwareProductCategoriesUrl() {
    const restCatalogPrefix = Configuration.get('restCatalogPrefix');
    return `${restCatalogPrefix}/v1/categories/resources/`;
}

function versionUrl(itemId, versionId) {
    const restPrefix = Configuration.get('restPrefix');
    return `${restPrefix}/v1.0/items/${itemId}/versions/${versionId}`;
}

function baseUrl(itemId, version, conflictId) {
    const versionId = version.id;
    const restPrefix = Configuration.get('restPrefix');
    let baseUrl = `${restPrefix}/v1.0/items/${itemId}/versions/${versionId}/conflicts`;
    return conflictId ? `${baseUrl}/${conflictId}` : baseUrl;
}

function fetchConflicts({ itemId, version }) {
    return RestAPIUtil.fetch(`${baseUrl(itemId, version)}`);
}

function fetchConflictById({ itemId, version, cid }) {
    return RestAPIUtil.fetch(`${baseUrl(itemId, version, cid)}`);
}

function resolveConflict({ itemId, version, conflictId, resolution }) {
    return RestAPIUtil.put(`${baseUrl(itemId, version, conflictId)}`, {
        resolution
    });
}

function fetchCategories() {
    return RestAPIUtil.fetch(softwareProductCategoriesUrl());
}

function fetchVersion({ vendorId, licensingVersion }) {
    return RestAPIUtil.fetch(versionUrl(vendorId, licensingVersion));
}

function createCategoryStr(data, { categories }) {
    let { category, subCategory } = data;
    let foundCat = categories.find(element => element.uniqueId === category);
    if (!foundCat) {
        return '';
    }

    let catName = foundCat.name;
    let foundSub = foundCat.subcategories.find(
        element => element.uniqueId === subCategory
    );
    if (!foundSub) {
        return `${catName}`;
    }

    let subcatName = foundSub.name;
    return `${catName} - ${subcatName}`;
}

function getEnumValues({ enums, list }) {
    if (!list) {
        return '';
    }
    return list.map(item => enums.find(el => el.enum === item).title);
}

const MergeEditorActionHelper = {
    analyzeSyncResult(dispatch, { itemId, version }) {
        return ItemsHelper.checkItemStatus(dispatch, {
            itemId,
            versionId: version.id
        }).then(response => {
            let inMerge =
                response &&
                response.state &&
                response.state.synchronizationState === SyncStates.MERGE;
            if (inMerge) {
                MergeEditorActionHelper.fetchConflicts(dispatch, {
                    itemId,
                    version
                }).then(() =>
                    dispatch({
                        type: modalActionTypes.GLOBAL_MODAL_SHOW,
                        data: {
                            modalComponentName: modalContentMapper.MERGE_EDITOR,
                            title: `${i18n('Merge Required')} - ${
                                version.description
                            }`,
                            onDeclined: () => {
                                dispatch({
                                    type: modalActionTypes.GLOBAL_MODAL_CLOSE
                                });
                            },
                            modalComponentProps: {
                                size: modalSizes.XLARGE
                            },
                            bodyClassName: 'merge-editor-modal'
                        }
                    })
                );
            }
            return Promise.resolve({
                updatedVersion: response,
                inMerge,
                isDirty: response.state.dirty
            });
        });
    },

    fetchConflicts(dispatch, { itemId, version }) {
        return fetchConflicts({ itemId, version }).then(data => {
            dispatch({
                type: actionTypes.LOAD_CONFLICTS,
                data
            });
            return data;
        });
    },

    fetchConflict(dispatch, { itemId, version, cid }) {
        fetchConflictById({ itemId, version, cid }).then(data => {
            let newData = {};
            newData = MergeEditorActionHelper.processConflict(dispatch, {
                conflict: data,
                itemId,
                cid,
                version
            });
            dispatch({
                type: actionTypes.LOAD_CONFLICT,
                data: newData
            });
        });
    },

    resolveConflict(
        dispatch,
        { itemId, version, conflictId, resolution, currentScreen }
    ) {
        resolveConflict({ itemId, version, conflictId, resolution }).then(
            () => {
                MergeEditorActionHelper.fetchConflicts(dispatch, {
                    itemId,
                    version
                }).then(conflicts => {
                    if (
                        conflicts.conflictInfoList &&
                        conflicts.conflictInfoList.length === 0
                    ) {
                        dispatch({
                            type: modalActionTypes.GLOBAL_MODAL_CLOSE
                        });
                        ScreensHelper.loadLandingScreen(dispatch, {
                            previousScreenName: currentScreen.screen,
                            props: currentScreen.props
                        });
                        ItemsHelper.checkItemStatus(dispatch, {
                            itemId,
                            versionId: version.id
                        });
                    }
                });
            }
        );
    },

    createConflictObject(
        data,
        { cid, conflict, dispatch, itemId, version, isYours }
    ) {
        let newData = {};

        for (let key in data) {
            if (data.hasOwnProperty(key)) {
                let value = data[key];
                let fieldRule =
                    (dataRules[conflict.type] &&
                        dataRules[conflict.type][key]) ||
                    dataRules.general[key];

                if (fieldRule) {
                    switch (fieldRule.rule) {
                        case rules.SKIP:
                            break;

                        case rules.BOOLEAN:
                            let { trueValue, falseValue } = fieldRule;
                            newData[key] =
                                value === trueValue
                                    ? true
                                    : value === falseValue ? false : undefined;
                            break;

                        case rules.PARSE:
                            let { moveFields, subFields } = fieldRule;
                            if (moveFields) {
                                let fields = subFields || Object.keys(value);
                                fields.forEach(field => {
                                    newData[
                                        field
                                    ] = MergeEditorActionHelper.createConflictObject(
                                        value[field],
                                        {
                                            cid,
                                            conflict,
                                            dispatch,
                                            itemId,
                                            version,
                                            isYours
                                        }
                                    );
                                });
                            } else {
                                newData[
                                    key
                                ] = MergeEditorActionHelper.createConflictObject(
                                    value,
                                    {
                                        cid,
                                        conflict,
                                        dispatch,
                                        itemId,
                                        version,
                                        isYours
                                    }
                                );
                            }
                            break;

                        case rules.FUNCTION:
                            let { args, functionName } = fieldRule;
                            newData[key] = MergeEditorActionHelper[
                                functionName
                            ](data, {
                                cid,
                                conflict,
                                dispatch,
                                version,
                                fieldName: key,
                                isYours,
                                itemId,
                                args
                            });
                            break;

                        default:
                            newData[key] = value;
                            break;
                    }
                } else {
                    newData[key] = value;
                }
            }
        }

        return newData;
    },

    getNamesFromIDs(
        data,
        { version, cid, dispatch, itemId, fieldName, isYours, args }
    ) {
        let idList = data[fieldName] || [];
        let { fetchFunction, fetchField } = args;

        let promises = idList.map(
            id =>
                new Promise(resolve =>
                    MergeEditorActionHelper[fetchFunction](dispatch, {
                        licenseModelId: itemId,
                        [fetchField]: id,
                        version
                    }).then(item => resolve(item.name))
                )
        );

        Promise.all(promises).then(fetchedItems => {
            let yoursOrTheirs = isYours ? 'yoursField' : 'theirsField';
            dispatch({
                type: actionTypes.DATA_PROCESSED,
                data: {
                    cid,
                    [yoursOrTheirs]: { name: fieldName, value: fetchedItems }
                }
            });
        });

        return idList;
    },

    getFeatureGroups(
        data,
        { version, cid, dispatch, itemId, fieldName, isYours }
    ) {
        let featureGroups = data[fieldName] || [];
        if (!(featureGroups instanceof Array)) {
            featureGroups = [featureGroups];
        }

        let promises = featureGroups.map(
            featureGroupId =>
                new Promise(resolve =>
                    FeatureGroupsActionHelper.fetchFeatureGroup(dispatch, {
                        licenseModelId: itemId,
                        featureGroupId,
                        version
                    })
                        .then(featureGroup => resolve(featureGroup.name))
                        .catch(reason =>
                            console.log(
                                `getFeatureGroups Promise rejected ('${reason}')`
                            )
                        )
                )
        );

        Promise.all(promises).then(fetchedGroups => {
            let yoursOrTheirs = isYours ? 'yoursField' : 'theirsField';
            dispatch({
                type: actionTypes.DATA_PROCESSED,
                data: {
                    cid,
                    [yoursOrTheirs]: { name: fieldName, value: fetchedGroups }
                }
            });
        });

        return featureGroups;
    },

    getLicenseAgreements(
        data,
        { version, cid, dispatch, itemId, fieldName, isYours }
    ) {
        let licenseAgreements = data[fieldName] || [];
        if (!(licenseAgreements instanceof Array)) {
            licenseAgreements = [licenseAgreements];
        }

        let promises = licenseAgreements.map(
            licenseAgreementId =>
                new Promise(resolve =>
                    LicenseAgreementActionHelper.fetchLicenseAgreement(
                        dispatch,
                        {
                            licenseModelId: itemId,
                            licenseAgreementId,
                            version
                        }
                    )
                        .then(licenseAgreement =>
                            resolve(licenseAgreement.name)
                        )
                        .catch(reason =>
                            console.log(
                                `getLicenseAgreements Promise rejected ('${reason}')`
                            )
                        )
                )
        );

        Promise.all(promises).then(fetchedAgreements => {
            let yoursOrTheirs = isYours ? 'yoursField' : 'theirsField';
            dispatch({
                type: actionTypes.DATA_PROCESSED,
                data: {
                    cid,
                    [yoursOrTheirs]: {
                        name: fieldName,
                        value: fetchedAgreements
                    }
                }
            });
        });

        return licenseAgreements;
    },

    processConflict(dispatch, { conflict, cid, version, itemId }) {
        let { id, type, yours, theirs } = conflict;

        let newYours = MergeEditorActionHelper.createConflictObject(
            cloneDeep(yours),
            { cid, conflict, dispatch, itemId, version, isYours: true }
        );
        let newTheirs = MergeEditorActionHelper.createConflictObject(
            cloneDeep(theirs),
            { cid, conflict, dispatch, itemId, version, isYours: false }
        );

        return {
            id,
            type,
            yours: newYours,
            theirs: newTheirs
        };
    },

    reduceList(data, { fieldName, args }) {
        let { subField } = args;
        return data[fieldName].map(el => el[subField]);
    },

    getEnumList({ fieldName }) {
        const enumLists = {
            licenseTerm: laOptionsValues.LICENSE_MODEL_TYPE,
            processType: processOptionValues.PROCESS_TYPE,
            limitType: [
                { title: 'Service Provider', enum: 'ServiceProvider' },
                { title: 'Vendor', enum: 'Vendor' }
            ],
            limitUnit: limitSelectValues.UNIT
        };

        return enumLists[fieldName];
    },

    getEnumValue(data, { fieldName, args = {} }) {
        let value = data[fieldName];
        let enumValues = MergeEditorActionHelper.getEnumList({
            fieldName: args.listName || fieldName
        });
        let enumValue = enumValues.find(el => el.enum === value);

        return (enumValue && enumValue.title) || value;
    },

    processChoice(data, { fieldName, args = {} }) {
        let value = data[fieldName];
        let enumValues = MergeEditorActionHelper.getEnumList({
            fieldName: args.listName || fieldName
        });
        let newValue =
            value.other ||
            (enumValues &&
                enumValues.find(el => el.enum === value.choice).title) ||
            value.choice;

        return newValue;
    },

    processChoices(data, { fieldName, args = {} }) {
        let value = data[fieldName];
        let enumValues = MergeEditorActionHelper.getEnumList({
            fieldName: args.listName || fieldName
        });
        let newValue =
            value.other ||
            getEnumValues({ enums: enumValues, list: value.choices }) ||
            value.choices;

        return newValue;
    },

    convertArrayToObject(data, { fieldName }) {
        let value = data[fieldName];
        let newValue = {};
        value.forEach((el, index) => {
            newValue[index] = el;
        });
        return newValue;
    },

    fetchCategory(data, { cid, isYours, fieldName, dispatch }) {
        fetchCategories().then(categories => {
            let value = createCategoryStr(data, { categories });
            let yoursOrTheirs = isYours ? 'yoursField' : 'theirsField';

            dispatch({
                type: actionTypes.DATA_PROCESSED,
                data: {
                    cid,
                    [yoursOrTheirs]: { name: fieldName, value }
                }
            });
        });
    },

    fetchLMVersion(data, { cid, dispatch, isYours }) {
        let { licensingVersion, vendorId } = data;
        let yoursOrTheirs = isYours ? 'yoursField' : 'theirsField';

        if (licensingVersion) {
            fetchVersion({ licensingVersion, vendorId }).then(response => {
                dispatch({
                    type: actionTypes.DATA_PROCESSED,
                    data: {
                        cid,
                        [yoursOrTheirs]: {
                            name: 'licensingVersion',
                            value: response.name
                        }
                    }
                });
            });
        }
    },

    parseDate(data, { fieldName }) {
        let date = data[fieldName];
        return date && moment(date, DATE_FORMAT).format(DATE_FORMAT);
    }
};

export default MergeEditorActionHelper;
